/*******************************************************************************
 * Copyright 2011-2012 Dik Grapendaal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package sh.grapendaal.tsuushin;

import java.io.File;

import javax.xml.bind.JAXBException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import sh.grapendaal.tsuushin.module.ModuleContainer;
import sh.grapendaal.tsuushin.module.ModuleContainerImpl;
import sh.grapendaal.tsuushin.service.BotService;
import sh.grapendaal.tsuushin.service.ConfigService;
import sh.grapendaal.tsuushin.service.ModuleService;
import sh.grapendaal.tsuushin.service.NetworkService;
import sh.grapendaal.tsuushin.service.PermissionService;
import sh.grapendaal.tsuushin.service.TrackingService;
import sh.grapendaal.tsuushin.service.impl.BotServiceImpl;
import sh.grapendaal.tsuushin.service.impl.ConfigServiceImpl;
import sh.grapendaal.tsuushin.service.impl.ModuleServiceImpl;
import sh.grapendaal.tsuushin.service.impl.NetworkServiceImpl;
import sh.grapendaal.tsuushin.service.impl.PermissionServiceImpl;
import sh.grapendaal.tsuushin.service.impl.TrackingServiceImpl;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class Tsuushin extends AbstractModule {
	private static final Logger LOGGER = LoggerFactory.getLogger(Tsuushin.class);

	private static final String OPTION_TEST_CONFIGURATION = "test";

	// FIXME Hardcoded. Make this a command line parameter, default to
	// "configuration.xml" in working directory.
	private static final String IRCBOT_CONFIGURATION_FILE = "configuration.xml";

	private static ConfigService configService;
	private static ModuleService moduleService;
	private static NetworkService networkService;
	private static BotService botService;

	@Override
	protected void configure() {
		bind(BotService.class).to(BotServiceImpl.class);
		bind(ConfigService.class).to(ConfigServiceImpl.class);
		bind(ModuleService.class).to(ModuleServiceImpl.class);
		bind(NetworkService.class).to(NetworkServiceImpl.class);
		bind(PermissionService.class).to(PermissionServiceImpl.class);
		bind(TrackingService.class).to(TrackingServiceImpl.class);
		bind(ModuleContainer.class).to(ModuleContainerImpl.class);
	}

	/**
	 * Entry point to the application.
	 * 
	 * @param args
	 *            Command line arguments.
	 */
	public static void main(String[] args) {
		LOGGER.debug("Starting application...");

		Injector injector = Guice.createInjector(new Tsuushin());

		moduleService = injector.getInstance(ModuleService.class);
		configService = injector.getInstance(ConfigService.class);
		networkService = injector.getInstance(NetworkService.class);
		botService = injector.getInstance(BotService.class);

		if (args.length > 0 && OPTION_TEST_CONFIGURATION.equals(args[0])) {
			((ConfigServiceImpl) configService).setRelativeWorkingDirectory("target");
		} else {
			// Prevent recursive logging while running in test mode.
			LogStream.setupLogger();
		}

		start();

		// Everything started nicely, now add the shutdown hook. If we did it
		// earlier, stop() would get called twice in case of an exception.
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			@Override
			public void run() {
				stop();
			}
		}, "Shutdown Hook"));

		LOGGER.debug("Done.");
	}

	/**
	 * Fire up all the services which make up the application. The rest will
	 * pick up from there.
	 */
	private static void start() {
		try {
			moduleService.start();
		} catch (Exception e) {
			LOGGER.error("Could not start the Module Service.", e);
			stop();
		}

		try {
			networkService.start();
		} catch (Exception e) {
			LOGGER.error("Could not start the Network Service.", e);
			stop();
		}
		
		try {
			configService.loadConfiguration(new File(IRCBOT_CONFIGURATION_FILE));
		} catch (JAXBException e) {
			LOGGER.error("Invalid configuration file: {}", e.getMessage(), e);
			stop();
		} catch (SAXException e) {
			LOGGER.error("Invalid configuration file: {}", e.getMessage(), e);
			stop();
		} catch (Exception e) {
			LOGGER.error("Could not parse configuration file.", e);
			stop();
		}

		try {
			botService.start();
		} catch (Exception e) {
			LOGGER.error("Could not start the Bot Service.", e);
			stop();
		}
	}

	/**
	 * Shuts down the services that need a clean shutdown.
	 */
	private static void stop() {
		LOGGER.debug("Stopping application...");

		botService.stop();
		moduleService.stop();
		networkService.stop();

		LOGGER.debug("Done.");
	}
}
